package com.example.run_game;
import java.util.ArrayList;
import java.util.Random;
import java.lang.Math;
import android.location.Location;
import android.location.LocationManager;
import android.util.Log;


public class Choice {
	private ArrayList<ChoicePair> choices;
	private Checkpoint goal;
	private ArrayList<Location> locs; 
	
	public Checkpoint getGoal()
	{
	  return goal;
	}
	
	public ChoicePair getGoalChoicePair()
	{
		return choices.get(getMaxWeightedChoiceIndex());
	}
	
	Choice(Checkpoint[] chkps, Location initialLoc, LocationManager locMan, double percentageComplete)
	{
		choices = new ArrayList<ChoicePair>();
		locs = new ArrayList<Location>();		
				
		int maxIndex =0;
		for(int i=0; i<chkps.length; i++)			
			if(chkps[i].location.distanceTo(initialLoc) > chkps[maxIndex].location.distanceTo(initialLoc) )
				maxIndex = i;		
		for(int i=0; i<chkps.length; i++)	
		{
		   boolean furthest = false;
		   if(i == maxIndex)
			   furthest = true;
		   choices.add(new ChoicePair(chkps[i], getBonusType(initialLoc, chkps[i].location, furthest, percentageComplete), initialLoc.bearingTo(chkps[i].location)));	 		  
		   Log.d("GAME: ", "Choose from: "+chkps[i].name);
		}
	}
	
	private String getBonusType(Location current, Location ckpLoc, boolean furthest, double percentageComplete)
    {
    	String[] strongBonuses = {"2xPoints", "runFast1000", "extra800"};
    	String[] weakBonuses = {"1.5xpoints", "extra500", "runFast800"};
    	Random rand = new Random();
    	int index = rand.nextInt(3);
    	int awardBonus = rand.nextInt(10);
    	
    	//Don't award a bonus half the time
    	if(awardBonus < 5)
    		return "";
    	//If the ckp is hard then pick from strong bonuses
    	if(isHardCkp(current, ckpLoc, furthest, percentageComplete))    	
    	  return strongBonuses[index];    	
    	//else from the weak bonuses 
    	return weakBonuses[index];
    }
    	
    private boolean isHardCkp(Location current, Location ckpLoc, boolean furthest, double percentageComplete)
	{
		if (ckpLoc.getAltitude() - current.getAltitude() > 8)
			return true;
		if( furthest)
			return true;
		if(percentageComplete > 70)
			return true;
		return false;
	}

	public Checkpoint updateLocModel(Location loc)
    {
    	locs.add(loc);
    	goal = getMostLikelyCheckpoint();
    	
    	//for(int i=0; i<choices.size(); i++)   
    		//Log.d("GAME:", choices.get(i).checkpoint.name+ ":  " +choices.get(i).weight);
    	
    	Log.d("GAME:", goal.name+" is likely goal: " + choices.get(getMaxWeightedChoiceIndex()).weight + " (" + loc.getLongitude() + "  " + loc.getLatitude() + ")   Distance To goal: "+ loc.distanceTo(goal.location));
    	if(inCkpRad(loc, goal))    	
    		return goal;
    	else    	
    		return null;    	
    }
    
    private boolean inCkpRad(Location loc, Checkpoint ckp)
    {
    	if(ckp != null)   
    		//Log.d("GAME: ", ckp.name+"  " +loc.distanceTo(ckp.location)+"<"+ckp.radius);
    		if(loc.distanceTo(ckp.location) < ckp.radius)
    			return true;        	
    	return false;
    }
    
    private Checkpoint getMostLikelyCheckpoint()
    {
    	//get the average bearing of all the currently recorded locations
    	double bearing =0;
    	for(int i=0; i< locs.size(); i++)    	
    		bearing += locs.get(0).bearingTo(locs.get(i));    	
    	bearing = bearing / locs.size();
    	
    	//sum the inverse of all differences and distances 
    	double diffBears[] = new double[choices.size()];
    	double closeness[] = new double[choices.size()];
    	double totalDiff = 0;
    	double totalCloseness =0;
    	for(int i=0; i<choices.size(); i++)
    	{
    		diffBears[i] = 1/Math.abs(bearing - choices.get(i).bearing);
    		closeness[i] = 1/locs.get(locs.size()-1).distanceTo(choices.get(i).checkpoint.location);
    		totalDiff += diffBears[i];  
    		totalCloseness += closeness[i];
    	}
    	//VARIOUS WEIGHTS, so adjust if need be
    	double diffFactor = 0.6 / totalDiff;
    	double closeFactor = 0.4 / totalCloseness;
    	
    	for(int i=0; i<choices.size(); i++)    	 
    	{
    		choices.get(i).weight = (diffBears[i] * diffFactor) + (closeness[i] * closeFactor);
    		if( Double.isNaN(choices.get(i).weight) )
    			choices.get(i).weight = 1;    			
    	}

    	return choices.get(getMaxWeightedChoiceIndex()).checkpoint;
    }

    private int getMaxWeightedChoiceIndex()
    {
    	int maxChoice = 0;
    	for(int i=0; i<choices.size(); i++)    	
    		if(choices.get(i).weight > choices.get(maxChoice).weight)
    			maxChoice = i;
    	return maxChoice;
    }
    
    public boolean bonusThresholdReached()
	{
		double distanceSoFar = locs.get(locs.size()-1).distanceTo(locs.get(0) );
		double distanceFromOrigToCkp =  locs.get(locs.size()-1).distanceTo(goal.location);
		if( distanceSoFar > 0.5 * distanceFromOrigToCkp )
			return true;
		if( choices.get(getMaxWeightedChoiceIndex()).weight > 0.5 )
			return true;
		return false;
	} 
    
    public Checkpoint[] getAllChoices()
    {
    	Checkpoint[] values = new Checkpoint[choices.size()];
    	for(int i=0; i<values.length; i++)
    	{
    		values[i] = choices.get(i).checkpoint;
    	}
    	return values;
    }

	public void ABonusRecieved()
	{
	   for(int i=0; i<choices.size(); i++)
		   choices.get(i).bonusUsed = true;
	}
	
	public String[] getAudioDescription()
	{
		ArrayList<String> stringList = new ArrayList<String>();
		String[] stringArray;    	
		
		if(choices.size() == 1)
			return new String[]{"Starting Point"};
		for(int i=0; i<choices.size(); i++)
    	{
			if(i == choices.size() -1)
				stringList.add("or");
    		stringList.add(choices.get(i).checkpoint.name);
    		if(choices.get(i).bonusType != "")
    			stringList.add(choices.get(i).bonusType);    		
    	}
		
		stringArray = new String[stringList.size()];
    	stringList.toArray(stringArray);   
    	for(int i=0; i<stringArray.length; i++)
    		Log.d("Audio:", stringArray[i]);    	
    	return stringArray;
	}


	public int getPlayerDirectionBearing()
	{		
		if(locs.size() >= 5)
		{
			int bearing =0;
			int topIndex = locs.size()-1;
			Location measurepoint = locs.get(topIndex -4);
			for(int i=0; i < 4; i++)
			{
				bearing += measurepoint.bearingTo(locs.get(topIndex-i));
			}
			bearing = bearing / 4;
			return bearing;
		}
		else
			return -1;
	}

}
